﻿// ******************************************************
// Copyright © 2010 DCOM Productions®, 
// All Rights Reserved
// ******************************************************

namespace BitFlex.EnterpriseServices.WindowsShell {
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using Microsoft.Win32;

    /// <summary>
    /// Provides methods and routines to enforce actions on various parts of the Windows Shell (Explorer)
    /// </summary>
    public static class WindowsShellService {

        #region Win32 API / Platform Invoke

        [DllImport("user32.dll")]
        private static extern bool EnableWindow(IntPtr hWnd, bool bEnable);

        [DllImport("user32.dll")]
        private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, IntPtr lpszClass, string lpszWindow);

        [DllImport("user32.dll")]
        private static extern bool PostMessage(IntPtr hWnd, int Msg, int wParam, int lParam);

        [DllImport("user32.dll")]
        private static extern int ShowWindow(IntPtr hWnd, int nCmdShow);

        #endregion

        #region Constants

        private const int SW_HIDE = 0;
        private const int SW_SHOW = 1;

        #endregion

        #region Properties

        private static DisableChangePasswordPolicy m_DisableChangePasswordPolicy = new DisableChangePasswordPolicy();
        /// <summary>
        /// Gets or sets the policy that prevents users from being able to change their password
        /// </summary>
        public static DisableChangePasswordPolicy DisableChangePasswordPolicy {
            get {
                return m_DisableChangePasswordPolicy;
            }
            set {
                m_DisableChangePasswordPolicy = value;
            }
        }

        private static DisableLockWorkstationPolicy m_DisableLockWorkstationPolicy = new DisableLockWorkstationPolicy();
        /// <summary>
        /// Gets or sets the policy that prevents users from locking the system
        /// </summary>
        public static DisableLockWorkstationPolicy DisableLockWorkstationPolicy {
            get {
                return m_DisableLockWorkstationPolicy;
            }
            set {
                m_DisableLockWorkstationPolicy = value;
            }
        }

        private static DisableTaskMgrPolicy m_DisableTaskMgrPolicy = new DisableTaskMgrPolicy();
        /// <summary>
        /// Gets or sets the policy that prevents users from accessing the Windows Task Manager
        /// </summary>
        public static DisableTaskMgrPolicy DisableTaskMgrPolicy {
            get {
                return m_DisableTaskMgrPolicy;
            }
            set {
                m_DisableTaskMgrPolicy = value;
            }
        }

        private static NoClosePolicy m_NoClosePolicy = new NoClosePolicy();
        /// <summary>
        /// Gets or sets the policy that prevents users from shutting down or restarting Windows
        /// </summary>
        public static NoClosePolicy NoClosePolicy {
            get {
                return m_NoClosePolicy;
            }
            set {
                m_NoClosePolicy = value;
            }
        }

        private static NoDesktopPolicy m_NoDesktopPolicy = new NoDesktopPolicy();
        /// <summary>
        /// Gets or sets the policy that hides all icons on the Windows Desktop
        /// </summary>
        public static NoDesktopPolicy NoDesktopPolicy {
            get {
                return m_NoDesktopPolicy;
            }
            set {
                m_NoDesktopPolicy = value;
            }
        }

        private static NoDrivesPolicy m_NoDrivesPolicy = new NoDrivesPolicy();
        /// <summary>
        /// Gets or sets the policy that removes the icons representing selected drives from My Computer and Windows Explorer. Also, the drive letters representing the selected drives do not appear in the standard Open dialog box
        /// </summary>
        public static NoDrivesPolicy NoDrivesPolicy {
            get {
                return m_NoDrivesPolicy;
            }
            set {
                m_NoDrivesPolicy = value;
            }
        }

        private static NoDriveTypeAutoRunPolicy m_NoDriveTypeAutoRunPolicy = new NoDriveTypeAutoRunPolicy();
        /// <summary>
        /// Gets or sets the policy that disables the AutoPlay feature
        /// </summary>
        public static NoDriveTypeAutoRunPolicy NoDriveTypeAutoRunPolicy {
            get {
                return m_NoDriveTypeAutoRunPolicy;
            }
            set {
                m_NoDriveTypeAutoRunPolicy = value;
            }
        }

        private static NoFavoritesMenuPolicy m_NoFavoritesMenuPolicy = new NoFavoritesMenuPolicy();
        /// <summary>
        /// Gets or sets the policy that prevents users from adding the Favorites menu to the Start menu.
        /// </summary>
        public static NoFavoritesMenuPolicy NoFavoritesMenuPolicy {
            get {
                return m_NoFavoritesMenuPolicy;
            }
            set {
                m_NoFavoritesMenuPolicy = value;
            }
        }

        private static NoFindPolicy m_NoFindPolicy = new NoFindPolicy();
        /// <summary>
        /// Gets or sets the policy that defines whether or not the shell removes or disables selected search features.
        /// </summary>
        public static NoFindPolicy NoFindPolicy {
            get {
                return m_NoFindPolicy;
            }
            set {
                m_NoFindPolicy = value;
            }
        }

        private static NoFolderOptionsPolicy m_NoFolderOptionsPolicy = new NoFolderOptionsPolicy();
        /// <summary>
        /// Gets or sets the policy that removes the Folder Options item from all Windows Explorer menus and removes the Folder Options item from Control Panel. As a result, users cannot use the Folder Options dialog box.
        /// </summary>
        public static NoFolderOptionsPolicy NoFolderOptionsPolicy {
            get {
                return m_NoFolderOptionsPolicy;
            }
            set {
                m_NoFolderOptionsPolicy = value;
            }
        }

        private static NoNetHoodPolicy m_NoNetHoodPolicy = new NoNetHoodPolicy();
        /// <summary>
        /// Gets or sets the policy that removes the "My Network Places" icon from the desktop
        /// </summary>
        public static NoNetHoodPolicy NoNetHoodPolicy {
            get {
                return m_NoNetHoodPolicy;
            }
            set {
                m_NoNetHoodPolicy = value;
            }
        }

        private static NoRecentDocsMenuPolicy m_NoRecentDocsMenuPolicy = new NoRecentDocsMenuPolicy();
        /// <summary>
        /// Gets or sets the policy that removes the Documents menu from the Start menu.
        /// </summary>
        public static NoRecentDocsMenuPolicy NoRecentDocsMenuPolicy {
            get {
                return m_NoRecentDocsMenuPolicy;
            }
            set {
                m_NoRecentDocsMenuPolicy = value;
            }
        }

        private static NoRunPolicy m_NoRunPolicy = new NoRunPolicy();
        /// <summary>
        /// Gets or sets the policy that removes the run menu from the start menu
        /// </summary>
        public static NoRunPolicy NoRunPolicy {
            get {
                return m_NoRunPolicy;
            }
            set {
                m_NoRunPolicy = value;
            }
        }

        private static NoSaveSettingsPolicy m_NoSaveSettingsPolicy = new NoSaveSettingsPolicy();
        /// <summary>
        /// Gets or sets the policy prevents users from saving certain changes to the desktop
        /// </summary>
        public static NoSaveSettingsPolicy NoSaveSettingsPolicy {
            get {
                return m_NoSaveSettingsPolicy;
            }
            set {
                m_NoSaveSettingsPolicy = value;
            }
        }

        private static NoSetActiveDesktopPolicy m_NoSetActiveDesktopPolicy = new NoSetActiveDesktopPolicy();
        /// <summary>
        /// Gets or sets the policy removes the Active Desktop options from Settings on the Start Menu.
        /// </summary>
        public static NoSetActiveDesktopPolicy NoSetActiveDesktopPolicy {
            get {
                return m_NoSetActiveDesktopPolicy;
            }
            set {
                m_NoSetActiveDesktopPolicy = value;
            }
        }

        private static NoSetFoldersPolicy m_NoSetFoldersPolicy = new NoSetFoldersPolicy();
        /// <summary>
        /// Gets or sets the policy that prevents Control Panel, Printers, and Network and Dial-up Connections from running
        /// </summary>
        public static NoSetFoldersPolicy NoSetFoldersPolicy {
            get {
                return m_NoSetFoldersPolicy;
            }
            set {
                m_NoSetFoldersPolicy = value;
            }
        }

        private static NoTrayContextMenuPolicy m_NoTrayContextMenuPolicy = new NoTrayContextMenuPolicy();
        /// <summary>
        /// Gets or sets the policy that disables the context menu for the Taskbar
        /// </summary>
        public static NoTrayContextMenuPolicy NoTrayContextMenuPolicy {
            get {
                return m_NoTrayContextMenuPolicy;
            }
            set {
                m_NoTrayContextMenuPolicy = value;
            }
        }

        private static NoViewContextMenuPolicy m_NoViewContextMenuPolicy = new NoViewContextMenuPolicy();
        /// <summary>
        /// Gets or sets the policy that disables Windows Explorer's default context menu
        /// </summary>
        public static NoViewContextMenuPolicy NoViewContextMenuPolicy {
            get {
                return m_NoViewContextMenuPolicy;
            }
            set {
                m_NoViewContextMenuPolicy = value;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Disables the Windows Start Button
        /// </summary>
        /// <remarks>This only applies to Windows Vista or above</remarks>
        public static void DisableStartButton() {
            if (Environment.OSVersion.Version.Major >= 6) {
                IntPtr handle;
                handle = FindWindowEx(IntPtr.Zero, IntPtr.Zero, (IntPtr)0xC017, "Start");
                EnableWindow(handle, false);
            }
            else {
                throw new DeprecatedPlatformException();
            }
        }

        /// <summary>
        /// Disables the Windows Taskbar
        /// </summary>
        public static void DisableTaskbar() {
            IntPtr handle;
            handle = FindWindow("Shell_TrayWnd", string.Empty);
            EnableWindow(handle, false);
        }

        /// <summary>
        /// Enables the Windows Start Button
        /// </summary>
        /// <remarks>This only applies to Windows Vista or above</remarks>
        public static void EnableStartButton() {
            if (Environment.OSVersion.Version.Major >= 6) {
                IntPtr handle;
                handle = FindWindowEx(IntPtr.Zero, IntPtr.Zero, (IntPtr)0xC017, "Start");
                EnableWindow(handle, true);
            }
            else {
                throw new DeprecatedPlatformException();
            }
        }

        /// <summary>
        /// Enables the Windows Taskbar
        /// </summary>
        public static void EnableTaskbar() {
            IntPtr handle;
            handle = FindWindow("Shell_TrayWnd", string.Empty);
            EnableWindow(handle, true);
        }

        private static void GetCurrentUserShell(out string shell) {
            const string keyLocation = @"Software\Microsoft\Windows NT\CurrentVersion\winlogon";
            RegistryKey key = null;
            try {
                key = Registry.CurrentUser.OpenSubKey(keyLocation);
                object value = key.GetValue("Shell", null);
                if (value != null) {
                    shell = value.ToString();
                }
                else {
                    shell = null;
                }
            }
            catch (System.ObjectDisposedException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.UnauthorizedAccessException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.Security.SecurityException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.IO.IOException ex) {
                throw new PolicyAccessException(ex);
            }
            finally {
                if (key != null) {
                    key.Close();
                }
            }
        }

        private static void GetLocalMachineShell(out string shell) {
            const string keyLocation = @"Software\Microsoft\Windows NT\CurrentVersion\winlogon";
            RegistryKey key = null;
            try {
                key = Registry.LocalMachine.OpenSubKey(keyLocation);
                object value = key.GetValue("Shell", null);
                if (value != null) {
                    shell = value.ToString();
                }
                else {
                    shell = null;
                }
            }
            catch (System.ObjectDisposedException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.UnauthorizedAccessException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.Security.SecurityException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.IO.IOException ex) {
                throw new PolicyAccessException(ex);
            }
            finally {
                if (key != null) {
                    key.Close();
                }
            }
        }

        /// <summary>
        /// Gets the shell from the Windows Registry and returns the path
        /// </summary>
        /// <param name="logon">The <see cref="Winlogon"/> enumeration value to get the shell for</param>
        /// <returns>Returns the full path or name of the shell</returns>
        public static string GetShell(Winlogon logon) {
            string shell = string.Empty;
            switch (logon) {
                case Winlogon.CurrentUser:
                    GetCurrentUserShell(out shell);
                    break;
                case Winlogon.LocalMachine:
                    GetLocalMachineShell(out shell);
                    break;
            }
            return shell;
        }

        /// <summary>
        /// Hides the Windows Start Button
        /// </summary>
        /// <remarks>This method only applies to Windows Vista or above.</remarks>
        public static void HideStartButton() {
            if (Environment.OSVersion.Version.Major >= 6) {
                IntPtr handle;
                handle = FindWindowEx(IntPtr.Zero, IntPtr.Zero, (IntPtr)0xC017, "Start");
                ShowWindow(handle, SW_HIDE);
            }
            else {
                throw new DeprecatedPlatformException();
            }
        }

        /// <summary>
        /// Hides the Windows Taskbar
        /// </summary>
        /// <remarks>
        /// In Windows XP Service Pack 3 or later operating platforms, the Windows Taskbar no longer is affected by
        /// SHAppBarMessage calls, and the working area of the desktop cannot be modified. What this means is that
        /// you can hide the Taskbar and Start Button, but the area reserved by the Taskbar will still stand, and
        /// cannot be changed.
        /// 
        /// The only work around for this, is that you set your application to TopMost so it overlaps the reserved
        /// space.
        /// </remarks>
        public static void HideTaskbar() {
            IntPtr handle;
            handle = FindWindow("Shell_TrayWnd", string.Empty);
            ShowWindow(handle, SW_HIDE);
        }

        private static void ResetCurrentUserShell() {
            const string keyLocation = @"Software\Microsoft\Windows NT\CurrentVersion\winlogon";
            RegistryKey key = null;
            try {
                key = Registry.CurrentUser.OpenSubKey(keyLocation, true);
                key.DeleteValue("Shell", false);
            }
            catch (System.ArgumentNullException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.ObjectDisposedException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.UnauthorizedAccessException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.Security.SecurityException ex) {
                throw new PolicyAccessException(ex);
            }
            finally {
                if (key != null) {
                    key.Close();
                }
            }
        }

        private static void ResetLocalMachineShell() {
            const string keyLocation = @"Software\Microsoft\Windows NT\CurrentVersion\winlogon";
            RegistryKey key = null;
            try {
                key = Registry.LocalMachine.OpenSubKey(keyLocation, true);
                key.SetValue("Shell", "explorer.exe");
            }
            catch (System.ArgumentNullException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.ObjectDisposedException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.UnauthorizedAccessException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.Security.SecurityException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.IO.IOException ex) {
                throw new PolicyAccessException(ex);
            }
            finally {
                if (key != null) {
                    key.Close();
                }
            }
        }

        /// <summary>
        /// Resets the Windows Shell back to Explorer.exe
        /// </summary>
        public static void ResetShell() {
            ResetCurrentUserShell();
            ResetLocalMachineShell();
        }

        private static void SetCurrentUserShell(string shell) {
            const string keyLocation = @"Software\Microsoft\Windows NT\CurrentVersion\winlogon";
            RegistryKey key = null;
            try {
                key = Registry.CurrentUser.OpenSubKey(keyLocation, true);
                key.SetValue("Shell", shell);
            }
            catch (System.ArgumentNullException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.ObjectDisposedException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.UnauthorizedAccessException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.Security.SecurityException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.IO.IOException ex) {
                throw new PolicyAccessException(ex);
            }
            finally {
                if (key != null) {
                    key.Close();
                }
            }
        }

        private static void SetLocalMachineShell(string shell) {
            const string keyLocation = @"Software\Microsoft\Windows NT\CurrentVersion\winlogon";
            RegistryKey key = null;
            try {
                key = Registry.LocalMachine.OpenSubKey(keyLocation, true);
                key.SetValue("Shell", shell);
            }
            catch (System.ArgumentNullException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.ObjectDisposedException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.UnauthorizedAccessException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.Security.SecurityException ex) {
                throw new PolicyAccessException(ex);
            }
            catch (System.IO.IOException ex) {
                throw new PolicyAccessException(ex);
            }
            finally {
                if (key != null) {
                    key.Close();
                }
            }
        }

        /// <summary>
        /// Sets the Windows Shell to use the specified shell application
        /// </summary>
        /// <param name="logon">The <see cref="Winlogon"/> enumeration value to set the shell application for</param>
        /// <param name="shell">The full path to the shell executable.</param>
        /// <remarks>
        /// The path to the shell program can be relative only if the application binary resides within the Windows directory.
        /// </remarks>
        public static void SetShell(Winlogon logon, string shell) {
            switch (logon) {
                case Winlogon.CurrentUser:
                    SetCurrentUserShell(shell);
                    break;
                case Winlogon.LocalMachine:
                    SetLocalMachineShell(shell);
                    break;
            }
        }

        /// <summary>
        /// Shows the Windows Start Button
        /// </summary>
        /// <remarks>This only applies to Windows Vista or above</remarks>
        public static void ShowStartButton() {
            if (Environment.OSVersion.Version.Major >= 6) {
                IntPtr handle;
                handle = FindWindowEx(IntPtr.Zero, IntPtr.Zero, (IntPtr)0xC017, "Start");
                ShowWindow(handle, SW_SHOW);
            }
            else {
                throw new DeprecatedPlatformException();
            }
        }

        /// <summary>
        /// Shows the Windows Taskbar
        /// </summary>
        public static void ShowTaskbar() {
            IntPtr handle;
            handle = FindWindow("Shell_TrayWnd", string.Empty);
            ShowWindow(handle, SW_SHOW);
        }

        #endregion
    }
}